﻿//------------------------------------------------------------------------------------------------------
// The MIT License (MIT) Copyright (c) 2014 Marton Rusko
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//------------------------------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Ninject.Infrastructure.Language;

namespace RM.Banking.Unicredit.Statements
{
    /// <summary>
    /// Base class for Unicredit statement rows
    /// </summary>
    public abstract class UnicreditStatementRecord
    {
        protected readonly string Line;

        //HACK: Fields initialized with reflection
#pragma warning disable 649

        /// <summary>
        /// Rekord típus, állandó rekord típusonként
        /// Cobol picture: 9(2)
        /// </summary>
        [UnicreditRowMapping(0, 2)]
        private string rekordTipus;

#pragma warning restore 649

        public UnicreditStatementRecordType RekordTipus { get; private set; }

        protected UnicreditStatementRecord(string line)
        {
            Line = line;
            LoadFields(line);

            RekordTipus = (UnicreditStatementRecordType)int.Parse(rekordTipus);
        }

        /// <summary>
        /// Loads fields attributed with UnicreditRowMappingAttribute
        /// </summary>
        /// <param name="line"></param>
        protected void LoadFields(string line)
        {
            var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
                                  .Union(GetType().BaseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
                                  .Where(f => f.HasAttribute<UnicreditRowMappingAttribute>())
                                  .ToArray();

            foreach (var fieldInfo in fields)
            {
                var attribute = fieldInfo.GetCustomAttributes(typeof(UnicreditRowMappingAttribute), false)
                    .OfType<UnicreditRowMappingAttribute>()
                    .First();

                if (attribute.Optional && (attribute.Index > line.Length || attribute.Index + attribute.Length > line.Length))
                    continue;

                fieldInfo.SetValue(this, line.Substring(attribute.Index, attribute.Length).Trim());
            }
        }

        /// <summary>
        /// Parse statement numeric string
        /// Example: Cobol picture: 9(13)V99 => 13 whole, 2 decimal places (hundreds)
        /// </summary>
        /// <param name="str">Numeric string</param>
        /// <param name="sign"></param>
        /// <param name="wholePlaces">Whole places</param>
        /// <param name="decimalPlaces">Decimal places</param>
        /// <returns>Double value</returns>
        internal double ParseAmount(string str, string sign = null, int wholePlaces = 13, int decimalPlaces = 2)
        {
            var whole = double.Parse(str.Substring(0, wholePlaces));
            var hundreds = double.Parse(str.Substring(wholePlaces, decimalPlaces)) / 100.0;

            var result = whole + hundreds;

            if (sign == "-")
                result = 0.0 - result;

            return result;
        }

        /// <summary>
        /// Parse statement date string (YYYYMMDD)
        /// </summary>
        /// <param name="str">Date string</param>
        /// <returns>DateTime value</returns>
        internal DateTime ParseDate(string str)
        {
            return new DateTime(int.Parse(str.Substring(0, 4)),
                int.Parse(str.Substring(4, 2)),
                int.Parse(str.Substring(6, 2)));
        }

    }
}